package com.googlecode.continuity.plugin.gwtext.client;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.*;

/**
 * TabBarImpl
 *
 * @author Peter Schmitt <aragos@aragos.de>
 */
public class TabBarImpl extends Composite implements TabBar {
// ------------------------------ FIELDS ------------------------------

  private static final PositionConstant POSITION_DEFAULT = POSITION_LEFT;
  private static final String STYLENAME_DEFAULT = "gwt-TabBarItem";
  private CellPanel panel;
  private PositionConstant position;
  private Widget selectedTab;
  private TabListenerCollection tabListeners;
  private HTML first;
  private HTML last;

// --------------------------- CONSTRUCTORS ---------------------------

  /**
   * Creates an empty tab bar with the default position.
   */
  public TabBarImpl() {
    this(POSITION_DEFAULT);
  }

  /**
   * Creates an empty tab bar with the given position.
   *
   * @param position tab bar position
   */
  public TabBarImpl(PositionConstant position) {
    setPosition(position);

    sinkEvents(Event.ONCLICK);
    setStyleName("gwt-TabBar");

  }

// --------------------- GETTER / SETTER METHODS ---------------------

  /**
   * Gets the position.
   *
   * @return the current position.
   */
  public PositionConstant getPosition() {
    return position;
  }

  /**
   * Sets the position.
   *
   * @param position the the position (
   *                 {@link HasPosition#POSITION_TOP},
   *                 {@link HasPosition# POSITION_RIGHT},
   *                 {@link HasPosition# POSITION_BOTTOM}, or
   *                 {@link HasPosition# POSITION_LEFT}).
   */
  public void setPosition(PositionConstant position) {
    if (position == this.position) {
      return;
    }

    this.position = position;

    CellPanel oldPanel = this.panel;
    // if/then/else instead of switch since enums are only supported in Java Tiger
    if (position == POSITION_TOP) {
      HorizontalPanel panelTop = new HorizontalPanel();
      panelTop.setVerticalAlignment(HasVerticalAlignment.ALIGN_BOTTOM);
      panel = panelTop;
    } else if (position == POSITION_RIGHT) {
      VerticalPanel panelRight = new VerticalPanel();
      panelRight.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
      panel = panelRight;
    } else if (position == POSITION_BOTTOM) {
      HorizontalPanel panelBottom = new HorizontalPanel();
      panelBottom.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP);
      panel = panelBottom;
    } else if (position == POSITION_LEFT) {
      VerticalPanel panelLeft = new VerticalPanel();
      panelLeft.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
      panel = panelLeft;
    }
    initWidget(panel);

    if (oldPanel != null) {
      while (oldPanel.iterator().hasNext()) {
        panel.add((Widget) oldPanel.iterator().next());
      }
    } else {
      first = new HTML("&nbsp;", true);
      last = new HTML("&nbsp;", true);
      first.setStyleName("gwt-TabBarFirst");
      last.setStyleName("gwt-TabBarLast");
      panel.add(first);
      panel.add(last);
    }

    if (isHorizontal()) {
      first.setHeight("100%");
      last.setHeight("100%");
      panel.setCellHeight(first, "100%");
      panel.setCellWidth(last, "100%");
    } else {
      first.setWidth("100%");
      last.setWidth("100%");
      panel.setCellWidth(first, "100%");
      panel.setCellHeight(last, "100%");
    }
  }

// ------------------------ INTERFACE METHODS ------------------------

// --------------------- Interface ClickListener ---------------------

  public void onClick(Widget sender) {
    for (int i = 1; i < panel.getWidgetCount() - 1; ++i) {
      if (panel.getWidget(i) == sender) {
        selectTab(i - 1);
        return;
      }
    }
  }

// --------------------- Interface SourcesTabEvents ---------------------

  public void addTabListener(TabListener listener) {
    if (tabListeners == null) {
      tabListeners = new TabListenerCollection();
    }
    tabListeners.add(listener);
  }

  public void removeTabListener(TabListener listener) {
    if (tabListeners != null) {
      tabListeners.remove(listener);
    }
  }

// --------------------- Interface TabBar ---------------------

  /**
   * Adds a new tab with the specified text.
   *
   * @param text the new tab's text
   */
  public void addTab(String text) {
    insertTab(text, getTabCount());
  }

  /**
   * Inserts a new tab at the specified index.
   *
   * @param text        the new tab's text
   * @param beforeIndex the index before which this tab will be inserted
   */
  public void insertTab(String text, int beforeIndex) {
    insertTab(text, false, beforeIndex);
  }

  /**
   * Gets the number of tabs present.
   *
   * @return the tab count
   */
  public int getTabCount() {
    return panel.getWidgetCount() - 2;
  }

  /**
   * Adds a new tab with the specified widget.
   *
   * @param widget the new tab's widget.
   */
  public void addTab(Widget widget) {
    insertTab(widget, getTabCount());
  }

  /**
   * Inserts a new tab at the specified index.
   *
   * @param widget      widget to be used in the new tab.
   * @param beforeIndex the index before which this tab will be inserted.
   */
  public void insertTab(Widget widget, int beforeIndex) {
    checkInsertBeforeTabIndex(beforeIndex);

    ClickDecoratorPanel decWidget = new ClickDecoratorPanel(widget, this);
    decWidget.addStyleName(STYLENAME_DEFAULT);
    if (isHorizontal()) {
      ((HorizontalPanel) panel).insert(decWidget, beforeIndex + 1);
    } else {
      ((VerticalPanel) panel).insert(decWidget, beforeIndex + 1);
    }
  }

  /**
   * Adds a new tab with the specified text.
   *
   * @param text   the new tab's text
   * @param asHTML <code>true</code> to treat the specified text as html
   */
  public void addTab(String text, boolean asHTML) {
    insertTab(text, asHTML, getTabCount());
  }

  /**
   * Inserts a new tab at the specified index.
   *
   * @param text        the new tab's text
   * @param asHTML      <code>true</code> to treat the specified text as HTML
   * @param beforeIndex the index before which this tab will be inserted
   */
  public void insertTab(String text, boolean asHTML, int beforeIndex) {
    checkInsertBeforeTabIndex(beforeIndex);

    Label item;
    if (asHTML) {
      item = new HTML(text);
    } else {
      item = new Label(text);
    }

    item.setWordWrap(false);
    item.addClickListener(this);
    item.setStyleName(STYLENAME_DEFAULT);
    if (isHorizontal()) {
      ((HorizontalPanel) panel).insert(item, beforeIndex + 1);
    } else {
      ((VerticalPanel) panel).insert(item, beforeIndex + 1);
    }
  }

  /**
   * Gets the tab that is currently selected.
   *
   * @return the selected tab
   */
  public int getSelectedTab() {
    if (selectedTab == null) {
      return -1;
    }
    return panel.getWidgetIndex(selectedTab) - 1;
  }

  /**
   * Gets the specified tab's HTML.
   *
   * @param index the index of the tab whose HTML is to be retrieved
   * @return the tab's HTML
   */
  public String getTabHTML(int index) {
    if (index >= getTabCount()) {
      return null;
    }
    Widget widget = panel.getWidget(index + 1);
    if (widget instanceof HTML) {
      return ((HTML) widget).getHTML();
    } else if (widget instanceof Label) {
      return ((Label) widget).getText();
    } else {
      // This will be a ClickDecorator holding a user-supplied widget.
      return DOM.getInnerHTML(widget.getElement());
    }
  }

  /**
   * Programmatically selects the specified tab. Use index -1 to specify that no
   * tab should be selected.
   *
   * @param index the index of the tab to be selected.
   * @return <code>true</code> if successful, <code>false</code> if the
   *         change is denied by the {@link com.google.gwt.user.client.ui.TabListener}.
   */
  public boolean selectTab(int index) {
    checkTabIndex(index);

    if (tabListeners != null) {
      if (!tabListeners.fireBeforeTabSelected(this, index)) {
        return false;
      }
    }

    // Check for -1.
    setSelectionStyle(selectedTab, false);
    if (index == -1) {
      selectedTab = null;
      return true;
    }

    selectedTab = panel.getWidget(index + 1);
    setSelectionStyle(selectedTab, true);

    if (tabListeners != null) {
      tabListeners.fireTabSelected(this, index);
    }
    return true;
  }

  /**
   * Removes the tab at the specified index.
   *
   * @param index the index of the tab to be removed
   */
  public void removeTab(int index) {
    checkTabIndex(index);

// (index + 1) to account for 'first' placeholder widget.
    Widget toRemove = panel.getWidget(index + 1);
    if (toRemove == selectedTab) {
      selectedTab = null;
    }
    panel.remove(toRemove);
  }

  public void setSelectionStyle(Widget item, boolean selected) {
    if (item != null) {
      if (selected) {
        item.addStyleName("gwt-TabBarItem-selected");
      } else {
        item.removeStyleName("gwt-TabBarItem-selected");
      }
    }
  }

  public void checkInsertBeforeTabIndex(int beforeIndex) {
    if ((beforeIndex < 0) || (beforeIndex > getTabCount())) {
      throw new IndexOutOfBoundsException();
    }
  }

  public void checkTabIndex(int index) {
    if ((index < -1) || (index >= getTabCount())) {
      throw new IndexOutOfBoundsException();
    }
  }

  public boolean isHorizontal() {
    return (position == POSITION_TOP || position == POSITION_BOTTOM);
  }

  public boolean isVertical() {
    return (position == POSITION_RIGHT || position == POSITION_LEFT);
  }

// -------------------------- INNER CLASSES --------------------------

  /**
   * <code>ClickDecoratorPanel</code> decorates any widget with the minimal
   * amount of machinery to receive clicks for delegation to the parent.
   * {@link com.google.gwt.user.client.ui.SourcesClickEvents} is not implemented due to the fact that only a
   * single observer is needed.
   */
  protected static final class ClickDecoratorPanel extends SimplePanel {
    ClickListener delegate;

    ClickDecoratorPanel(Widget child, ClickListener delegate) {
      this.delegate = delegate;
      setWidget(child);
      sinkEvents(Event.ONCLICK);
    }

    public void onBrowserEvent(Event event) {
      // No need for call to super.
      switch (DOM.eventGetType(event)) {
        case Event.ONCLICK:
          delegate.onClick(this);
      }
    }
  }
}
